package br.ufc.apsoo.controllers;

import java.util.Date;
import java.util.List;

import br.com.caelum.vraptor.Get;
import br.com.caelum.vraptor.Path;
import br.com.caelum.vraptor.Post;
import br.com.caelum.vraptor.Resource;
import br.com.caelum.vraptor.Result;
import br.com.caelum.vraptor.Validator;
import br.com.caelum.vraptor.validator.ValidationMessage;
import br.ufc.apsoo.annotation.NaoInterceptavel;
import br.ufc.apsoo.exceptions.HospedeJaCadastradoException;
import br.ufc.apsoo.models.Apartamento;
import br.ufc.apsoo.models.Despesa;
import br.ufc.apsoo.models.Hospede;
import br.ufc.apsoo.models.Reserva;
import br.ufc.apsoo.models.Situacao;
import br.ufc.apsoo.repositories.TodasReservas;
import br.ufc.apsoo.repositories.TodosApartamentos;
import br.ufc.apsoo.repositories.TodosHospedes;
import br.ufc.apsoo.repositories.TodosPaises;
import br.ufc.apsoo.repositories.TodosTiposApartamento;

import com.google.common.base.Strings;

@Resource
public class ReservaController {

	private final Result result;

	private final Validator validator;

	private final TodasReservas todasReservas;

	private final TodosApartamentos todosApartamentos;

	private final TodosHospedes todosHospedes;

	private final TodosTiposApartamento todosTiposApartamento;

	private final TodosPaises todosPaises;

	public ReservaController(Result result, Validator validator,
			TodasReservas todasReservas, TodosApartamentos todosApartamentos,
			TodosHospedes todosHospedes, TodosPaises todosPaises,
			TodosTiposApartamento todosTiposApartamento) {
		this.validator = validator;
		this.result = result;
		this.todasReservas = todasReservas;
		this.todosApartamentos = todosApartamentos;
		this.todosHospedes = todosHospedes;
		this.todosTiposApartamento = todosTiposApartamento;
		this.todosPaises = todosPaises;
	}

	@Get
	@Path("/reserva/lista/apartamentos")
	public List<Apartamento> apartamentosDisponiveis(Reserva reserva) {
		validaBusca(reserva);
		validator.onErrorForwardTo(this).index();
		result.include("reserva", reserva);
		result.include("tipos", todosTiposApartamento.lista());
		return todosApartamentos.naoReservados(reserva);
	}

	@Get
	@Path("/reserva/checkin/{reserva.id}")
	public void checkin(Reserva reserva) {
		reserva = todasReservas.trazerPorId(reserva.getId());
		validaCheckin(reserva);
		validator.onErrorRedirectTo(this).lista();
		result.include("reserva", reserva);
		result.include("paises", todosPaises.doMundo());
	}

	@Get
	@Path("/reserva/checkout/{reserva.id}")
	public Reserva checkout(Reserva reserva) {
		reserva = todasReservas.trazerPorId(reserva.getId());
		if (!Situacao.OCUPADO.equals(reserva.getSituacao())) {
			validator.add(new ValidationMessage(
					"Não foi feito o checkin, impossível fazer checkout!",
					"erro"));
		}
		validator.onErrorRedirectTo(this).lista();

		List<Despesa> despesas = reserva.getDespesas();
		result.include("despesas", despesas);

		return reserva;
	}

	@Get
	@Path("/reserva/confirma/apartamento/{reserva.apartamento.numero}/entrada/{reserva.dataEntrada*}/saida/{reserva.dataSaida*}")
	public void confirma(Reserva reserva) {
		reserva.setApartamento(todosApartamentos.trazPorNumero(reserva
				.getApartamento().getNumero()));
		result.include("reserva", reserva);
		result.include("paises", todosPaises.doMundo());
	}

	@Post
	@Path("/reserva/confirma/checkin")
	public void confirmaCheckin(Reserva reserva) {
		validaHospede(reserva.getHospede());
		if (!validator.hasErrors()) {
			try {
				todosHospedes.atualiza(reserva.getHospede());
			} catch (HospedeJaCadastradoException e) {
				validator.add(new ValidationMessage(e.getMessage(), "erro"));
			}
		}
		if (!validator.hasErrors()) {
			todasReservas.checkin(reserva);
			result.include("message", "Checkin efetuado com sucesso!")
					.redirectTo(this).lista();
		} else {
			result.include("reserva", reserva);
			result.include("paises", todosPaises.doMundo());
			validator.onErrorUsePageOf(this).checkin(reserva);
		}
	}

	@Post
	@Path("/reserva/confirma/checkout")
	public void confirmaCheckout(Reserva reserva) {
		todasReservas.checkout(reserva);
		result.include("message", "Checkout efetuado com sucesso!")
				.redirectTo(this).lista();
	}

	@NaoInterceptavel
	@Get
	@Path("/reserva/online/confirma/apartamento/{reserva.apartamento.numero}/entrada/{reserva.dataEntrada*}/saida/{reserva.dataSaida*}")
	public void confirmaOnline(Reserva reserva) {
		reserva.setApartamento(todosApartamentos.trazPorNumero(reserva
				.getApartamento().getNumero()));
		result.include("reserva", reserva);
		result.include("paises", todosPaises.doMundo());
	}

	@NaoInterceptavel
	@Get
	@Path("/reserva/online/lista/apartamentos")
	public List<Apartamento> disponibilidadeOnline(Reserva reserva) {
		validaBusca(reserva);
		validator.onErrorForwardTo(this).online();
		result.include("reserva", reserva);
		result.include("tipos", todosTiposApartamento.lista());
		return todosApartamentos.naoReservados(reserva);
	}

	@Get
	@Path("/reserva")
	public void index() {
		result.include("tipos", todosTiposApartamento.lista());
	}

	@Get
	@Path("/reserva/lista")
	public List<Reserva> lista() {
		return todasReservas.naoLivres();
	}

	@NaoInterceptavel
	@Get
	@Path("/reserva/online")
	public void online() {
		result.include("tipos", todosTiposApartamento.lista());
	}

	@Post
	@Path("/reserva")
	public void realiza(Reserva reserva) {
		Hospede hospede = reserva.getHospede();
		if (reserva.getHospede().getId() != null
				&& !reserva.getHospede().getId().equals(0)) {
			hospede = todosHospedes.porId(reserva.getHospede().getId());
			hospede.setNome(reserva.getHospede().getNome());
			hospede.setPais(reserva.getHospede().getPais());
			hospede.setCpf(reserva.getHospede().getCpf());
			hospede.setPassaporte(reserva.getHospede().getPassaporte());
		}

		validaHospede(hospede);
		if (!validator.hasErrors()) {
			salvaHospede(hospede);
		} else {
			validator.onErrorForwardTo(this).confirma(reserva);
		}
		hospede = todosHospedes.porEmail(reserva.getHospede().getEmail());

		reserva.setHospede(hospede);
		reserva.reservar();
		validator.validate(reserva);
		validator.onErrorForwardTo(this).confirma(reserva);
		todasReservas.realizar(reserva);
		result.include("message", "Reserva realizada com sucesso!")
				.redirectTo(this).index();
	}

	@NaoInterceptavel
	@Post
	@Path("/reserva/online")
	public void realizaOnline(Reserva reserva) {
		Hospede hospede = reserva.getHospede();
		if (reserva.getHospede().getId() != null
				&& !reserva.getHospede().getId().equals(0)) {
			hospede = todosHospedes.porId(reserva.getHospede().getId());
			hospede.setNome(reserva.getHospede().getNome());
			hospede.setPais(reserva.getHospede().getPais());
			hospede.setCpf(reserva.getHospede().getCpf());
			hospede.setPassaporte(reserva.getHospede().getPassaporte());
		}

		validaHospede(hospede);
		if (!validator.hasErrors()) {
			salvaHospede(hospede);
		} else {
			validator.onErrorForwardTo(this).confirmaOnline(reserva);
		}
		hospede = todosHospedes.porEmail(reserva.getHospede().getEmail());

		reserva.setHospede(hospede);
		reserva.reservar();
		validator.validate(reserva);
		validator.onErrorForwardTo(this).confirmaOnline(reserva);
		todasReservas.realizar(reserva);
		result.include("message", "Reserva realizada com sucesso!")
				.redirectTo(this).online();
	}

	private void salvaHospede(Hospede hospede) {
		try {
			if (hospede.getId() == null || hospede.getId().equals(0)) {
				todosHospedes.cadastra(hospede);
			} else {
				todosHospedes.atualiza(hospede);
			}
		} catch (HospedeJaCadastradoException e) {
			validator.add(new ValidationMessage(e.getMessage(), "erro"));
		}
	}

	private void validaBusca(Reserva reserva) {
		Date dataEntrada = reserva.getDataEntrada();
		Date dataSaida = reserva.getDataSaida();

		if (dataEntrada == null) {
			validator.add(new ValidationMessage(
					"Data de entrada deve ser preenchida!", "erro"));
		}

		if (dataSaida == null) {
			validator.add(new ValidationMessage(
					"Data de saída deve ser preenchida!", "erro"));
		}

		if (dataEntrada != null && dataSaida != null
				&& dataEntrada.after(dataSaida)) {
			validator.add(new ValidationMessage(
					"Data de saída deve ser maior que a data de entrada!",
					"erro"));
		}
	}

	private void validaCheckin(Reserva reserva) {
		if (reserva.getDataEntrada().after(new Date())) {
			validator.add(new ValidationMessage(
					"Não é possível fazer checkin antes da data da reserva!",
					"erro"));
		}

		if (Situacao.OCUPADO.equals(reserva.getSituacao())) {
			validator.add(new ValidationMessage(
					"Já foi feito checkin desta reserva!", "erro"));
		}
	}

	private void validaHospede(Hospede hospede) {
		validator.validate(hospede);
		if (hospede.getPais() == null || hospede.getPais().getId().equals(0)) {
			if (Strings.isNullOrEmpty(hospede.getCpf())) {
				validator.add(new ValidationMessage(
						"Hospedes brasileiros devem informar um CPF!", "erro"));
			}
		} else {
			if (Strings.isNullOrEmpty(hospede.getPassaporte())) {
				validator
						.add(new ValidationMessage(
								"Hospedes estrangeiros devem informar um numero de passaporte!",
								"erro"));
			}
		}
	}

}
